package com.warmheart.base.dao;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;

import com.warmheart.base.entity.BaseEntity;
import com.warmheart.base.page.PageInfo;
import com.warmheart.base.page.Pager;
import com.warmheart.core.constant.Globals;
import com.warmheart.core.rs.enums.SmTypeEnum;
import com.warmheart.core.rs.model.GroupByCntResultModel;

public abstract class BaseDao<T extends BaseEntity<?, ?>> {

    @Autowired
    protected SqlSessionTemplate sqlSessionTemplate;

    private String packageName;

    private String className;

    /**
     * 实例化过程中根据泛型获取必要参数,从而对应命名空间
     */
    public BaseDao() {
        Type entity = this.getClass().getGenericSuperclass();
        if (ParameterizedType.class.isAssignableFrom(entity.getClass())) {
            Type[] typeArr = ((ParameterizedType) entity).getActualTypeArguments();
            if (typeArr != null && typeArr.length > 0) {
                Type actualType = typeArr[0];
                if (Class.class.isAssignableFrom(actualType.getClass())) {
                    Class<?> cls = (Class<?>) actualType;
                    this.className = cls.getSimpleName();
                    String allPackageName = cls.getPackage().getName();
                    this.packageName = allPackageName.substring(allPackageName.lastIndexOf(Globals.DOT) + 1);
                } else if (ParameterizedType.class.isAssignableFrom(actualType.getClass())) {
                    Type rawType = ((ParameterizedType) actualType).getRawType();
                    if (Class.class.isAssignableFrom(rawType.getClass())) {
                        Class<?> cls = (Class<?>) rawType;
                        this.className = cls.getSimpleName();
                        String allPackageName = cls.getPackage().getName();
                        this.packageName = allPackageName.substring(allPackageName.lastIndexOf(Globals.DOT) + 1);
                    }
                }
            }
        }
    }

    /**
     * 指向读取数据
     * 
     * @return
     */
    private String getReadSql() {
        return "read."
                + (StringUtils.isNotBlank(this.packageName) ? (this.packageName.toLowerCase() + Globals.DOT) : "")
                + (StringUtils.isNotBlank(this.className) ? (this.className.toLowerCase() + Globals.DOT) : "");
    }

    /**
     * 指向修改数据
     * 
     * @return
     */
    private String getWriteSql() {
        return "write."
                + (StringUtils.isNotBlank(this.packageName) ? (this.packageName.toLowerCase() + Globals.DOT) : "")
                + (StringUtils.isNotBlank(this.className) ? (this.className.toLowerCase() + Globals.DOT) : "");
    }

    /**
     * 
     * @Title: queryById
     * @Description: 这里使用MAP是为了兼容不同类型的主键
     * @author ZhangJianyang
     * @param id
     * @return
     */
    public <PK> T queryById(PK id) {
        Map<String, PK> param = new HashMap<String, PK>();
        param.put("id", id);
        return sqlSessionTemplate.selectOne(getReadSql() + "queryById", param);
    }

    /**
     * 
     * @Title: queryByIds
     * @Description: 根据ID列表批量查询数据列表，这里使用MAP是为了兼容不同类型的主键
     * @author ZhangJianyang
     * @param ids
     * @return
     */
    public <PK> List<T> queryByIds(PK[] ids) {
        if (ids == null || ids.length == 0) {
            return new ArrayList<T>();
        }
        Map<String, PK[]> param = new HashMap<String, PK[]>();
        param.put("ids", ids);
        return sqlSessionTemplate.selectList(getReadSql() + "queryByIds", param);
    }

    /**
     * 通用单条记录查询
     * 
     * @param entity
     * @return
     */
    public T selectOne(T entity) {
        return sqlSessionTemplate.selectOne(getReadSql() + "selectOne", entity);
    }
    
    /**
     * 
     * @Title: selectOnlyOne
     * @Description: 通用查询最多只查询一条记录
     * @author ZhangJianyang
     * @param entity
     * @return
     */
    public T selectOnlyOne(T entity) {
        return sqlSessionTemplate.selectOne(getReadSql() + "selectOnlyOne", entity);
    }
    
    /**
     * 
     * @Title: selectCombineOne
     * @Description: 通用联合查询单条记录
     * @author ZhangJianyang
     * @param entity
     * @return
     */
    public Map<String, Object> selectCombineOne(T entity) {
        return sqlSessionTemplate.selectOne(getReadSql() + "selectCombineOne", entity);
    }

    /**
     * 通用列表查询
     * 
     * @param entity
     * @return
     */
    public List<T> selectList(T entity) {
        return sqlSessionTemplate.selectList(getReadSql() + "selectList", entity);
    }
    
    /**
     * 
     * @Title: selectCombineList
     * @Description: 通用联合查询列表
     * @author ZhangJianyang
     * @param entity
     * @return
     */
    public List<Map<String, Object>> selectCombineList(T entity) {
        // 设置多字段查询
        entity.getJoinModel().getDistinctModel().setSmType(SmTypeEnum.MULTIPLE.getValue());
        // 查询数据
        return sqlSessionTemplate.selectList(getReadSql() + "selectCombineList", entity);
    }

    /**
     * 通用列表数量查询
     * 
     * @param entity
     * @return
     */
    public int queryByCount(T entity) {
        return sqlSessionTemplate.selectOne(getReadSql() + "queryByCount", entity);
    }
    
    /**
     * 
     * @Title: queryCombineCount
     * @Description: 通用联合查询列表数量
     * @author ZhangJianyang
     * @param entity
     * @return
     */
    public int queryCombineCount(T entity) {
        return sqlSessionTemplate.selectOne(getReadSql() + "queryCombineCount", entity);
    }

    /**
     * 通用分页查询
     * 
     * @param entity
     * @return
     */
    public PageInfo<T> queryByList(T entity) {
        Integer rowCount = queryByCount(entity);
        Pager pager = entity.getPager();
        pager.setRowCount(rowCount);
        List<T> dataList = sqlSessionTemplate.selectList(getReadSql() + "queryByList", entity);
        return new PageInfo<T>(dataList, pager);
    }
    
    /**
     * 
     * @Title: queryCombineList
     * @Description: 通用分页联合查询
     * @author ZhangJianyang
     * @param entity
     * @return
     */
    public PageInfo<Map<String, Object>> queryCombineList(T entity) {
        Integer rowCount = queryCombineCount(entity);
        Pager pager = entity.getPager();
        pager.setRowCount(rowCount);
        List<Map<String, Object>> dataList = sqlSessionTemplate.selectList(getReadSql() + "queryCombineList", entity);
        return new PageInfo<Map<String, Object>>(dataList, pager);
    }

    /**
     * 自定义查询单体
     * 
     * @param param
     * @param customMethod
     * @return
     */
    @SuppressWarnings("unchecked")
    public <RS, P> RS selectOneCustom(P param, String customMethod) {
        return (RS) sqlSessionTemplate.selectOne(getReadSql() + customMethod.trim(), param);
    }

    /**
     * 自定义查询列表
     * 
     * @param param
     * @param customMethod
     * @return
     */
    public <RS, P> List<RS> selectListCustom(P param, String customMethod) {
        return sqlSessionTemplate.selectList(getReadSql() + customMethod.trim(), param);
    }

    /**
     * 
     * @Title: queryByListCustom
     * @Description: 自定义分页查询，通常用于参数和返回值不是同一类型，尤其是返回值使用map的情况
     * @author ZhangJianyang
     * @param param
     * @param customData
     * @param customCnt
     * @return
     */
    public <RS, P extends BaseEntity<?, ?>> PageInfo<RS> queryByListCustom(P param, String customData, String customCnt) {
        Integer rowCount = selectOneCustom(param, customCnt);
        Pager pager = param.getPager();
        pager.setRowCount(rowCount);
        List<RS> dataList = sqlSessionTemplate.selectList(getReadSql() + customData, param);
        return new PageInfo<RS>(dataList, pager);
    }

    /**
     * 
     * @Title: selectSingleList
     * @Description: 查询单个字段列表
     * @author ZhangJianyang
     * @param entity
     * @return
     */
    public <F> List<F> selectSingleList(T entity, Boolean... distinctFlags) {
        // 构建单字段查询包装
        entity.rsBuilder().singleWarpper(distinctFlags);
        // 根据核心数据查询信息
        return sqlSessionTemplate.selectList(getReadSql() + "selectSingleList", entity);
    }

    /**
     * 
     * @Title: selectMultipleList
     * @Description: 查询多个字段列表
     * @author ZhangJianyang
     * @param entity
     * @return
     */
    public List<Map<String, Object>> selectMultipleList(T entity, Boolean... distinctFlags) {
        // 构建多字段查询包装
        entity.rsBuilder().multipleWarpper(distinctFlags);
        return sqlSessionTemplate.selectList(getReadSql() + "selectMultipleList", entity);
    }

    /**
     * 
     * @Title: selectKeyValueList
     * @Description: 查询KEY以及VALUE映射列表
     * @author ZhangJianyang
     * @param entity
     * @param distinctFlags
     * @return
     */
    public List<Map<String, Object>> selectKeyValueList(T entity, Boolean... distinctFlags) {
        // 构建KEY以及VALUE映射列表查询包装
        entity.rsBuilder().keyValue(distinctFlags);
        return sqlSessionTemplate.selectList(getReadSql() + "selectKeyValueList", entity);
    }

    /**
     * 
     * @Title: selectGroupByCntList
     * @Description: 指定字段分组查询数量
     * @author ZhangJianyang
     * @param entity
     * @param distinctFlags
     * @return
     */
    public <F> List<GroupByCntResultModel<F>> selectGroupByCntList(T entity, Boolean... distinctFlags) {
        // 构建分组字段数量查询包装
        entity.rsBuilder().groupByCnt(distinctFlags);
        // 根据核心数据查询信息
        return sqlSessionTemplate.selectList(getReadSql() + "selectGroupByCntList", entity);
    }

    /**
     * 插入对象
     * 
     * @param param
     */
    public void add(T param) {
        sqlSessionTemplate.insert(getWriteSql() + "add", param);
    }

    /**
     * 修改数据，修改实体类中不为null的字段数据,用于避免0倍识别为空字符串
     * 
     * @param param
     */
    public void update(T param) {
        sqlSessionTemplate.update(getWriteSql() + "update", param);
    }

    /**
     * 修改数据，只修改实体类中不为空的字段数据
     * 
     * @param param
     */
    public void updateBySelective(T param) {
        sqlSessionTemplate.update(getWriteSql() + "updateBySelective", param);
    }

    /**
     * 修改数据，修改所有字段，不论为null还是空字符串
     * 
     * @param param
     */
    public void updateIncludeAll(T param) {
        sqlSessionTemplate.update(getWriteSql() + "updateIncludeAll", param);
    }

    /**
     * 
     * @Title: deleteById
     * @Description: 这里使用MAP是为了兼容不同类型的主键
     * @author ZhangJianyang
     * @param id
     */
    public <PK> void deleteById(PK id) {
        Map<String, PK> param = new HashMap<String, PK>();
        param.put("id", id);
        sqlSessionTemplate.delete(getWriteSql() + "deleteById", param);
    }

    /**
     * 
     * @Title: deleteByIds
     * @Description: 根据ID批量删除
     * @author HQKJ
     * @param ids
     */
    public <PK> void deleteByIds(PK[] ids) {
        if (ids == null || ids.length == 0) {
            return;
        }
        Map<String, PK[]> param = new HashMap<String, PK[]>();
        param.put("ids", ids);
        sqlSessionTemplate.delete(getWriteSql() + "deleteByIds", param);
    }

    /**
     * 根据条件彻底删除数据
     * 
     * @param param
     */
    public void deleteBySelective(T param) {
        sqlSessionTemplate.delete(getWriteSql() + "deleteBySelective", param);
    }

    /**
     * 
     * @Title: deleteLogicById
     * @Description: 这里使用MAP是为了兼容不同类型的主键
     * @author ZhangJianyang
     * @param id
     */
    public <PK> void deleteLogicById(PK id) {
        Map<String, PK> param = new HashMap<String, PK>();
        param.put("id", id);
        sqlSessionTemplate.delete(getWriteSql() + "deleteLogicById", param);
    }

    /**
     * 
     * @Title: deleteLogicByIds
     * @Description: 这里使用MAP是为了兼容不同类型的主键
     * @author ZhangJianyang
     * @param ids
     */
    public <PK> void deleteLogicByIds(PK[] ids) {
        if (ids == null || ids.length == 0) {
            return;
        }
        Map<String, PK[]> param = new HashMap<String, PK[]>();
        param.put("ids", ids);
        sqlSessionTemplate.delete(getWriteSql() + "deleteLogicByIds", param);
    }

    /**
     * 根据条件逻辑删除数据
     * 
     * @param param
     */
    public void deleteLogicBySelective(T param) {
        sqlSessionTemplate.delete(getWriteSql() + "deleteLogicBySelective", param);
    }

    /**
     * 自定义插入操作
     * 
     * @param param
     * @param customMethod
     */
    public <P> void addEntityCustom(P param, String customMethod) {
        sqlSessionTemplate.insert(getWriteSql() + customMethod.trim(), param);
    }

    /**
     * 自定义更新操作
     * 
     * @param param
     * @param customMethod
     */
    public <P> void updateEntityCustom(P param, String customMethod) {
        sqlSessionTemplate.update(getWriteSql() + customMethod.trim(), param);
    }

    /**
     * 自定义删除操作
     * 
     * @param param
     * @param customMethod
     */
    public <P> void deleteEntityCustom(P param, String customMethod) {
        sqlSessionTemplate.delete(getWriteSql() + customMethod.trim(), param);
    }
}
